package com.wang.cloudstorage;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONArray;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.util.JSON;
import com.wang.cloudstorage.db.DAOFactory;
import com.wang.cloudstorage.db.dao.INodeDAO;

public class ACL 
{
	public static int default_permission = INodeDAO.ACL_ALL_DENY;

	static Logger logger = LoggerFactory.getLogger(ACL.class);

	FSClient fsClient = null;
	public FSClient getFsClient() {
		return fsClient;
	}
	public void setFsClient(FSClient fsClient) {
		this.fsClient = fsClient;
	}

	DAOFactory daoFactory = null;
	public DAOFactory getDAOFactory() {
		return daoFactory;
	}
	public void setDAOFactory(DAOFactory daoFactory) {
		this.daoFactory = daoFactory;
	}
	
	public void deleteACL(String path) throws JSONException
	{
		String iNodeString = fsClient.getINode(path);
		INodeDAO iNodeDAO = daoFactory.getINodeDAO(iNodeString);
		iNodeDAO.deleteACL();
	}

	public JSONObject getACL(String path) throws JSONException {
		String iNodeString = fsClient.getINode(path);
		INodeDAO iNodeDAO = daoFactory.getINodeDAO(iNodeString);
		return iNodeDAO.getACL();
	}

	protected int computePermit(int permit)
	{
		if(INodeDAO.ACL_ALL_DENY == (permit & INodeDAO.ACL_ALL_DENY))
		{
			permit = INodeDAO.ACL_ALL_DENY;
		}
		else if(INodeDAO.ACL_ALL_ALLOWED == (permit & INodeDAO.ACL_ALL_ALLOWED))
		{
			permit = INodeDAO.ACL_ALL_ALLOWED;
		}
		else if(permit == INodeDAO.ACL_UNKNOWN)
		{
			permit = INodeDAO.ACL_UNKNOWN;
		}
		else if(permit > INodeDAO.ACL_UNKNOWN)
		{
			int upper = ((permit ^ (permit << (Integer.SIZE/2-1))) & permit);
			int lower = ((permit ^ (permit >> (Integer.SIZE/2-1))) & permit);
			permit = upper & lower;
		}

		return permit;
	}

	public void addAdministrator(String path, String admin) throws JSONException
	{
		String iNodeString = fsClient.getINode(path);
		INodeDAO iNodeDAO = daoFactory.getINodeDAO(iNodeString);
		JSONObject aclObject = iNodeDAO.getACL();
		
		Object admins = aclObject.get("administrators");
		
		if(JSONObject.NULL == admins)
		{
			admins = new BasicDBList();
		}

		if(!((BasicDBList) admins).contains(admin))
		{
			((BasicDBList) admins).add(admin);
			iNodeDAO.updateAdministrators(admins);
		}
	}
	
	public void deleteAdministrator(String path, String userId) throws JSONException
	{
		String iNodeString = fsClient.getINode(path);
		INodeDAO iNodeDAO = daoFactory.getINodeDAO(iNodeString);
		JSONObject aclObject = iNodeDAO.getACL();
		
		Object admins = aclObject.get("administrators");
		if(JSONObject.NULL != admins)
		{
			List<Object> adminList = toList((JSONArray) admins);
			if(adminList.contains(userId))
			{
				adminList.remove(userId);
				iNodeDAO.updateAdministrators(toArray(adminList));
			}
		}
	}
	

	public void setPermission(String path, String userId, int permit) throws JSONException
	{
		String iNodeString = fsClient.getINode(path);
		INodeDAO iNodeDAO = daoFactory.getINodeDAO(iNodeString);
		
		Object permissions = null;
		JSONObject acl = iNodeDAO.getACL();
		if(null == acl)
		{
			iNodeDAO.createACL(null);
			permissions = new JSONObject();
		}
		else
		{
			permissions = acl.get("permissions");
			if(JSONObject.NULL == permissions)
			{
				permissions = new JSONObject();
			}
		}
		
		permit = computePermit(permit);
		if(permit >= 0)
		{
			((JSONObject)permissions).put(userId, permit);
			iNodeDAO.updatePermission(permissions.toString());
		}
	}

	public int getPermission(String iNode, String userId) throws JSONException
	{
		INodeDAO iNodeDAO = daoFactory.getINodeDAO(iNode);
		int permission = iNodeDAO.getPermission(userId);
		if(INodeDAO.ACL_UNKNOWN != permission)
		{
			return permission;
		}
		
		return default_permission;
	}

	public void deletePermission(String path, String userId) throws JSONException
	{
		String iNodeString = fsClient.getINode(path);
		INodeDAO iNodeDAO = daoFactory.getINodeDAO(iNodeString);
		iNodeDAO.deletePermission(userId);
	}

	//
	public boolean isAllowed(String path, String userId, int want) throws JSONException
	{
		JSONObject userInfo = getUserInformation(userId);
		Object groups = userInfo.get("groups");
		Object roles = userInfo.get("roles");

		String[] iNodes = fsClient.getINodePath(path);
		for(String iNode: iNodes)
		{
			INodeDAO iNodeDAO = daoFactory.getINodeDAO(iNode);
			
			JSONObject acl = iNodeDAO.getACL();
			if(null == acl)
			{
				return want == default_permission;
			}

			// Check admin list.
			Object list = acl.get("administrators");
			if(JSONObject.NULL != list)
			{
				int length = ((JSONArray)list).length();
				for(int i=0; i<length; i++ )
				{
					String admin = ((JSONArray)list).getString(i);
					
					// Check username
					if(admin.equals(userId))
					{
						return true;
					}
					
					// Check groups
					if(JSONObject.NULL != groups)
					{
						int groupLength = ((JSONArray)groups).length();
						for(int j=0; j<groupLength; j++)
						{
							String groupId = ((JSONArray)groups).getString(j);
							if(admin.equals(groupId))
							{
								return true;
							}
						}
					}
				}
			}
			
			// Check permission
			list = acl.get("permissions");
			if(JSONObject.NULL != list)
			{
				// Check username
				Object perm = ((JSONObject)list).get(userId);
				if(JSONObject.NULL != perm)
				{
					if(want == ((Integer)perm & want) || (Integer)perm == INodeDAO.ACL_ALL_ALLOWED)
					{
						return true;
					}
				}
				
				// Check groups
				if(JSONObject.NULL != groups)
				{
					int groupLength = ((JSONArray)groups).length();
					for(int j=0; j<groupLength; j++)
					{
						String groupId = ((JSONArray)groups).getString(j);
						perm = ((JSONObject)list).get(groupId);
						if(JSONObject.NULL != perm)
						{
							if(want == ((Integer)perm & want) || (Integer)perm == INodeDAO.ACL_ALL_ALLOWED)
							{
								return true;
							}
						}
					}
				}
			}
		}

		return want == default_permission;
	}
	
	protected JSONObject getUserInformation(String userId) throws JSONException
	{
		BasicDBObject userInfoBson = new BasicDBObject().append("userId", userId);
		userInfoBson.append("groups", null);
		userInfoBson.append("roles", null);
		
		return (new JSONObject(JSON.serialize(userInfoBson)));
	}
	
	//
	private static List<Object> toList(final JSONArray ja) throws JSONException {
		final int len = ja.length();
		final List<Object> results = new ArrayList<Object>(len);
		for (int i = 0; i < len; i++) {
			final Object obj = ja.get(i);
			if (obj != null) {
				results.add(obj);
			}
		}
		return results;
	}
	
	private static JSONArray toArray(List<Object> list)
	{
		final JSONArray ja = new JSONArray();
	    for (final Object obj : list) {
	        ja.put(obj);
	    }
	    
	    return ja;
	}
}
